package character.race;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

import utilities.MinMaxPair;

/**
 * Race properties class.
 * @author pcdejager
 *
 */
public class RaceProperties {
    /**
     * Hide default constructor.
     */
    protected RaceProperties() {
        readEntries();
    }

    /**
     * Read the entries from XML.
     */
    private void readEntries() {
        entries = new HashMap<String, Race>();

        try {
            File file = new File("src\\main\\resources\\Race.xml");
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(file);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getElementsByTagName("race");
            for (int count = 0; count < nodeList.getLength(); ++count) {
                Node item = nodeList.item(count);
                String name = item.getAttributes().getNamedItem("name")
                        .getTextContent();

                Node ages = ((Element) item).getElementsByTagName("ages")
                        .item(0);
                int max = Integer.parseInt(ages.getAttributes()
                        .getNamedItem("max").getTextContent());

                Node partner = ((Element) ages).getElementsByTagName(
                        "partnerAge").item(0);
                int partnerStart = Integer.parseInt(partner.getAttributes()
                        .getNamedItem("start").getTextContent());
                int minC = Integer.parseInt(partner.getAttributes()
                        .getNamedItem("minConstant").getTextContent());
                double minM = Double.parseDouble(partner.getAttributes()
                        .getNamedItem("minMultiplier").getTextContent());
                int maxC = Integer.parseInt(partner.getAttributes()
                        .getNamedItem("maxConstant").getTextContent());
                double maxM = Double.parseDouble(partner.getAttributes()
                        .getNamedItem("maxMultiplier").getTextContent());

                Node rates = ((Element) item).getElementsByTagName("rates")
                        .item(0);
                double fertility = ((double)
                        Integer.parseInt(rates.getAttributes().
                                getNamedItem("fertility").getTextContent()))
                        / ((double) defaultRate);
                double mortality = ((double)
                        Integer.parseInt(rates.getAttributes()
                                .getNamedItem("mortality").getTextContent()))
                        / ((double) defaultRate);
                double love = ((double) Integer.parseInt(rates.getAttributes()
                        .getNamedItem("love").getTextContent()))
                        / ((double) defaultRate);
                double marry = ((double) Integer.parseInt(rates.getAttributes()
                        .getNamedItem("marry").getTextContent()))
                        / ((double) defaultRate);

                Node mat = ((Element) item).getElementsByTagName("maternity")
                        .item(0);
                int pregMin = Integer.parseInt(mat.getAttributes()
                        .getNamedItem("pregnantMin").getTextContent());
                int pregMax = Integer.parseInt(mat.getAttributes()
                        .getNamedItem("pregnantMax").getTextContent());
                int matMin = Integer.parseInt(mat.getAttributes()
                        .getNamedItem("maternityMin").getTextContent());
                int matMax = Integer.parseInt(mat.getAttributes()
                        .getNamedItem("maternityMax").getTextContent());
                double twins = Double.parseDouble(mat.getAttributes()
                        .getNamedItem("twins").getTextContent());

                Node attribNode = ((Element) item)
                        .getElementsByTagName("attribs").item(0);
                Node heightNode = ((Element) attribNode)
                        .getElementsByTagName("height").item(0);
                int maleHeightMin = Integer.parseInt(heightNode
                        .getAttributes().getNamedItem("maleMin")
                        .getTextContent());
                int maleHeightMax = Integer.parseInt(heightNode
                        .getAttributes().getNamedItem("maleMax")
                        .getTextContent());
                int femaleHeightMin = Integer.parseInt(heightNode
                        .getAttributes().getNamedItem("femaleMin")
                        .getTextContent());
                int femaleHeightMax = Integer.parseInt(heightNode
                        .getAttributes().getNamedItem("femaleMax")
                        .getTextContent());

                GenderMinMaxValues<Integer> heightAttrib =
                        new GenderMinMaxValues<Integer>(
                                new MinMaxPair<Integer>(
                                        maleHeightMin,
                                        maleHeightMax),
                                new MinMaxPair<Integer>(
                                        femaleHeightMin,
                                        femaleHeightMax));
                entries.put(name,
                        new Race(
                                name,
                                max,
                                new RaceMaternity(
                                        new MinMaxPair<Integer>(pregMin,
                                                pregMax),
                                        new MinMaxPair<Integer>(matMin,
                                                matMax),
                                        twins),
                                new RacePartner(
                                        partnerStart,
                                        new MinMaxPair<Integer>(minC,
                                                maxC),
                                        new MinMaxPair<Double>(minM,
                                                maxM)),
                                new RaceRates(
                                        fertility, mortality, love, marry),
                                new RacePhysicalAttributes(heightAttrib)));
            }
        } catch (Exception e) {
            entries = null;
        }
    }

    /**
     * Get the race information given the name.
     * @param race The name
     * @return The race information. If not found, returns null
     */
    public final Race get(final String race) {
        return entries.get(race);
    }

    /**
     * Get all the races.
     * @return The race names
     */
    public final String[] getRaces() {
        String[] result = new String[entries.size()];
        int pos = 0;
        for (String race : entries.keySet()) {
            result[pos] = race;
            ++pos;
        }
        return result;
    }

    /**
     * Get the instance.
     * @return The instance
     */
    public static RaceProperties getInstance() {
        if (instance == null) {
            instance = new RaceProperties();
        }
        return instance;
    }

    /**
     * The singleton instance of the class.
     */
    private static RaceProperties instance = null;

    /**
     * The map of races.
     */
    private Map<String, Race> entries = null;
    /**
     * The default rate (value per x).
     */
    private final int defaultRate = 1000;
}
